﻿#region Using

using System;
using System.Collections.Generic;
using System.IO;

using DotNetMatrix;

using Emgu.CV;
using Emgu.CV.Structure;

#endregion

namespace EigenFaces {
	public class TraitHandler {
		public Matrix AverageImage { get; set; }
		public Matrix EigenFaces { get; set; }

		public Matrix GetTraits(Image<Gray, double> image) {
			return GetTraits(image.ToVector());
		}

		public Matrix GetTraits(Matrix imageVector) {
			return EigenFaces * (imageVector - AverageImage);
		}

		public void MakeEigenFacesFromPath(String path) {
			var gammaVectors = GammaVectorsFromPath(path);

			CalculateAverageImage(gammaVectors);

			var phiVectors = new List<Matrix>();

			foreach( var gamma in gammaVectors ) {
				phiVectors.Add((gamma - AverageImage).Normalize());
			}

			Matrix matA = phiVectors.ToMatrix();
			Matrix matAT = matA.Transpose();

			Matrix matL = matAT * matA;

			EigenFaces = (matL.Eigen().GetVectors() * matAT).Normalize();
		}

		private static List<Matrix> GammaVectorsFromPath(string path) {
			var files = Directory.GetFiles(path, "*.jpg");
			var gammaVectors = new List<Matrix>();

			foreach( var file in files ) {
				var recog = new FaceRecognizer(file);
				for( int i = 0; i < recog.NumberOfFaces; i++ ) {
					gammaVectors.Add(recog.getFaceImage(i).ToVector());
				}
			}

			return gammaVectors;
		}

		private void CalculateAverageImage(List<Matrix> gammaVectors) {
			AverageImage = null;

			foreach( var gamma in gammaVectors ) {
				if( AverageImage == null ) {
					AverageImage = new Matrix(gamma.RowDimension, gamma.ColumnDimension);
				}

				AverageImage += gamma / gammaVectors.Count;
			}
		}

		public void ReadFrom(BinaryReader reader) {
			AverageImage = Matrix.ReadFrom(reader);
			EigenFaces = Matrix.ReadFrom(reader);
		}

		public void WriteTo(BinaryWriter writer) {
			AverageImage.WriteTo(writer);
			EigenFaces.WriteTo(writer);
		}
	}
}
